/*
 * Trait.cpp
 *
 *  Created on: Dec 28, 2008
 *      Author: sez
 */

#include "Trait.h"
#include "TraitException.h"
#include "CharacterRecord.h"

#include <QVariant>

#include <Xml/Xml.h>
#include <Xml/XmlPath.h>

Trait::Trait(const XmlPath &path)
    : TitledData(path.objectName())
    , base(path.type())
    , baseTypeName(path.typeName())
{
}

Trait::Trait(const QString &title)
	: TitledData(title)
{
}

Trait::Trait(const Trait &rhs)
	: TitledData(rhs.title())
{
	this->details = rhs.details;
	this->summary = rhs.summary;
}

Trait::~Trait()
{
}

void Trait::saveToXml() const
{
    XmlPath path(base, XmlPath::safeDataPathName(title()));
    path.setSubType(XmlPath::TRAIT_TYPE);
    Xml xml(path);
    xml.appendTag("Title", title());
    xml.appendTag("BaseTypeName", baseTypeName);
    xml.appendTag("Details", details);
    xml.appendTag("Summary", summary);
    xml.appendArray("Instructions", instructions);
    xml.serializeXml();
}

void Trait::loadFromXml()
{
    XmlPath path(base, XmlPath::safeDataPathName(title()));
    path.setSubType(XmlPath::TRAIT_TYPE);
    Xml xml(path);
    xml.deserializeXml();
    setTitle(xml.value("Title"));
    baseTypeName = xml.value("BaseTypeName");
    details = xml.value("Details");
    summary = xml.value("Summary");
    instructions = xml.valueArray("Instructions");
}

Trait &Trait::operator=(const Trait &rhs)
{
	this->details = rhs.details;
	this->summary = rhs.summary;
	return *this;
}

/*!
  \brief Apply the trait to the \a record.
  \details The instructions in the Trait are made up of lines like
    \code
    initiativeFeat = 1
    implement += staff
    speedArmourPenalty -= 1
    \endcode
    or generally <property> <operator> <value>.

    Valid operators are "=", "+=" and "-=".

    Values must be either a number or a string.  If the property type
    requires a number, and the value cannot be parsed as a number (by
    QString::toInt()) then a warning is printed and the value is
    skippped.  Otherwise the value is used as a string.
  */
void Trait::apply(CharacterRecord *record) const
{
    QStringList::ConstIterator it = instructions.constBegin();
    for ( ; it != instructions.constEnd(); ++it )
    {
        QStringList ops = (*it).split(QRegExp("\\s+"));
        QString prop = ops[0];
        QString op = ops[1];
        QString val = ops[2];
        int i = 0;
        bool validInt = false;
        const char *p = prop.toLocal8Bit().constData();
        QVariant current = record->property(p);
        if (current.isValid())
            throw TraitException(QObject::tr("Property name not found: \"%1\" in %2").arg(prop).arg(*it));
        if (current.type() == QVariant::Int)
        {
            i = val.toInt(&validInt);
            if (!validInt)
                throw TraitException(QObject::tr("Could not parse int: \"%1\" in %2").arg(val).arg(*it));
            int cur = current.toInt(&validInt);
            Q_ASSERT(validInt);
            if (op == "=")
                record->setProperty(p, i);
            else if (op == "+=")
                record->setProperty(p, cur + i);
            else if (op == "-=")
                record->setProperty(p, cur - i);
            else
                throw TraitException(QObject::tr("Could not parse operator: \"%1\" in %2").arg(op).arg(*it));
        }
        else if (current.type() == QVariant::String)
        {
            QString v = current.toString();
            if (op == "=")
                record->setProperty(p, val);
            else if (op == "+=")
                record->setProperty(p, v + " " + val);
            else if (op == "-=")
                throw TraitException(QObject::tr("Operator -= not supported for strings: %1").arg(*it));
            else
                throw TraitException(QObject::tr("Could not parse operator: \"%1\" in %2").arg(op).arg(*it));
        }
        else
        {
            throw TraitException(QObject::tr("Unexpected type in property: %1").arg(current.typeName()));
        }
    }
}
